package com.atguigu.gmall.serviceutil.aspect;

import com.atguigu.gmall.common.constans.RedisConstant;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.serviceutil.annotation.AppCache;
import com.atguigu.gmall.serviceutil.service.CacheService;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Map;

/**
 * @author A
 * @version 1.0
 * @description: TODO
 * @date 2025/9/3 3:00
 */
@Slf4j
@Component
public class CacheHelper {
    @Autowired
    private CacheService cacheService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private Map<String,RBloomFilter<Object>> bloomFilterMap;
    //获取注解上CacheKey的SpEL表达式
    public String getParserCacheKey(MethodSignature signature){
        Method method = signature.getMethod();
        AppCache annotation = AnnotationUtils.getAnnotation(method, AppCache.class);
        if (annotation == null) {
            return null; //没有注解，不走缓存
        }
        return annotation.cacheKey();
    }
    //获取注解上的布隆过滤器名称
    public String getParserBloomFilterName(MethodSignature signature){
        Method method = signature.getMethod();
        AppCache annotation = AnnotationUtils.getAnnotation(method, AppCache.class);
        if (annotation == null) {
            return null; //没有注解，name为空
        }
        return annotation.bloomName();
    }
    //获取注解上的布隆过滤器SpEL表达式
    public String getParserBloomFilterValue(MethodSignature signature){
        Method method = signature.getMethod();
        AppCache annotation = AnnotationUtils.getAnnotation(method, AppCache.class);
        if (annotation == null) {
            return null; //没有注解
        }
        return annotation.bloomValue();
    }
    //查询缓存
    public Object getCacheData(Object[] args, MethodSignature signature) {
        Method method = signature.getMethod();
        Type type = method.getGenericReturnType(); //真正返回值类型
        String parserCacheKey = getParserCacheKey(signature); //获取注解上的SpEL表达式
        String cacheKey = getSpelValue(args, parserCacheKey, String.class); //通过SpEL表达式获取缓存key
        if (cacheKey == null) return null; //没有缓存key，不走缓存
        return cacheService.getCacheData(cacheKey, new TypeReference<Object>() {
            @Override
            public Type getType() {
                return type;
            }
        });
    }
    //通过SpEL表达式获取缓存key
    private static <T> T getSpelValue(Object[] args, String parserCacheKey, Class<T> tClass) {
        SpelExpressionParser parser = new SpelExpressionParser();
        if (parserCacheKey.isEmpty()) {
            log.error("SpEL表达式为空");
            return null;
        }        //解析SpEL表达式
        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
        evaluationContext.setVariable("args", args);
        String date = DateUtil.formatDate(new Date());
        evaluationContext.setVariable("currentDate",date);
        return  parser.parseExpression(parserCacheKey, ParserContext.TEMPLATE_EXPRESSION).getValue(evaluationContext,tClass);
    }

    //验证布隆过滤器
    public Boolean checkBloomFilter(Object[] args, MethodSignature signature) {
        String bloomFilterName = getParserBloomFilterName(signature);
        RBloomFilter<Object> bloomFilter = bloomFilterMap.get(bloomFilterName);
        if (bloomFilter == null) {
            log.error("没有布隆过滤器:{}", bloomFilterName);
            return false; //没有布隆过滤器
        }
        String bloomFilterValue = getParserBloomFilterValue(signature);
        if (bloomFilterValue.isEmpty()) {
            log.error("没有布隆过滤器值:{}", bloomFilterValue);
            return false; //没有布隆过滤器值
        }
        Object bloomCheckId = getSpelValue(args, bloomFilterValue, Object.class);
        log.info("布隆过滤器{}校验id:{}", bloomFilterName, bloomCheckId);
        return bloomFilter.contains(bloomCheckId);
    }

    //尝试加锁
    public Boolean tryLock(Object[] args, MethodSignature signature) {
        String lockKey = getLockKey(args, signature);
        RLock lock = redissonClient.getLock(lockKey);
        boolean tryLock = lock.tryLock();
        if (tryLock) {
            log.info("args:{}的锁加锁成功,锁key:{}", args, lockKey);
        }else {
            log.error("args:{}的锁加锁失败,锁key:{}", args, lockKey);
        }
        return tryLock;
    }
    //获取锁key:  lock:缓存key
    private String getLockKey(Object[] args, MethodSignature signature) {
        String parserCacheKey = getParserCacheKey(signature); //获取注解上的SpEL表达式
        String spelValue = getSpelValue(args, parserCacheKey, String.class); //通过SpEL表达式获取缓存key
        return RedisConstant.LOCK_PREFIX+spelValue;
    }

    //解锁
    public void unlock(Object[] args, MethodSignature signature) {
        String lockKey = getLockKey(args, signature);
        RLock lock = redissonClient.getLock(lockKey);
        if (lock.isLocked()){
            lock.unlock();
            log.info("args:{}的锁解锁成功,锁key:{}", args, lockKey);
        }else {
            log.error("args:{}的锁解锁失败,锁key:{}", args, lockKey);
        }

    }
    //保存缓存
    public void saveCacheData(Object[] args, Object result, MethodSignature signature) {
        String parserCacheKey = getParserCacheKey(signature);
        String cacheKey = getSpelValue(args, parserCacheKey, String.class);
        cacheService.setCacheData(cacheKey, result);
    }
    //获取布隆过滤器名称
    public String checkUseBloomFilter(MethodSignature signature) {
        return getParserBloomFilterName(signature);
    }
}
